package com.medistat.dialog.services;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;

import com.medistat.dialog.ScheduleCell;
import com.medistat.dialog.ScheduleSelection;
import com.medistat.model.Doctor;
import com.medistat.model.Work;
import com.medistat.model.enums.Days;
import com.medistat.model.enums.TypeOfWork;
import com.medistat.persistance.service.interfaces.PersistanceService;
import com.medistat.util.DateUtils;

public abstract class ScheduleCreator implements Creator {

	protected PersistanceService persistanceService;

	@Autowired
	public void setPersistanceService(PersistanceService persistanceService) {
		this.persistanceService = persistanceService;
	}

	@Override
	public List<com.medistat.dialog.Schedule> createSchedule(Calendar date) throws Exception {
		if (date == null) {
			return Collections.emptyList();
		}
		Calendar firstMondayInThisMonth = DateUtils.getFirstDateOfFirstWeekInThisMonth(date);
		Calendar lastDate = firstMondayInThisMonth;
		Integer year = DateUtils.getYearFromDate(date);
		Integer month = DateUtils.getMonthFromDate(date);
		Integer numberOfWeeksInMonth = DateUtils.getNumberOfWeeksInMonth(date);
		Integer weekInYear = DateUtils.getWeekInYearFrom(date);
		List<com.medistat.dialog.Schedule> schedules = new ArrayList<com.medistat.dialog.Schedule>();
		beforeCreation(year, date);
		for (int week = weekInYear; week < (weekInYear + numberOfWeeksInMonth); week++) {
			List<Calendar> dates = DateUtils.generateSevenDatesStartingWith(lastDate);
			com.medistat.dialog.Schedule schedule = new com.medistat.dialog.Schedule();
			schedule.setWeek(week);
			schedule.setYear(year);
			schedule.setScheduleSelections(createScheduleSelections(dates, month));
			schedules.add(schedule);
			lastDate = DateUtils.getNextDateFrom(dates.get(6));
		}
		secondPass(schedules);
		afterCreation();
		return schedules;
	}

	private List<com.medistat.dialog.ScheduleSelection> createScheduleSelections(List<Calendar> dates, Integer month) throws Exception {
		List<com.medistat.dialog.ScheduleSelection> scheduleSelections = new ArrayList<ScheduleSelection>();
		for (TypeOfWork typeOfWork : TypeOfWork.values()) {
			com.medistat.dialog.ScheduleSelection scheduleSelection = new com.medistat.dialog.ScheduleSelection();
			scheduleSelection.setTypeOfWork(typeOfWork);
			List<ScheduleCell> scheduleCells = createScheduleCells(dates, typeOfWork, month, scheduleSelection);
			scheduleSelection.setScheduleCells(scheduleCells);
			scheduleSelections.add(scheduleSelection);

		}
		return scheduleSelections;
	}

	private List<com.medistat.dialog.ScheduleCell> createScheduleCells(List<Calendar> dates, TypeOfWork typeOfWork, Integer month, ScheduleSelection parent) throws Exception {
		List<com.medistat.dialog.ScheduleCell> scheduleCells = new ArrayList<ScheduleCell>();
		int dateCounter = 0;
		for (Days day : Days.values()) {
			com.medistat.dialog.ScheduleCell scheduleCell = new ScheduleCell(parent);
			scheduleCell.setDay(day);
			Calendar date = dates.get(dateCounter++);
			scheduleCell.setDate(date);
			Doctor doctor = null;
			doctor = findDoctor(typeOfWork, month, scheduleCell, date);
			scheduleCell.setDoctor(doctor);
			scheduleCell.setDoctorId(doctor == null ? null : doctor.getId());
			scheduleCells.add(scheduleCell);
		}
		return scheduleCells;
	}
	
	
	protected Doctor findDoctor(TypeOfWork typeOfWork, Integer month, com.medistat.dialog.ScheduleCell scheduleCell, Calendar date) throws Exception {
		Doctor doctor;
		if (DateUtils.isDateFromThisMonth(date, month)) {
			doctor = findAvailableDoctor(date, typeOfWork, scheduleCell);
		} else {
			doctor = findDoctorScheduledOnDateFromPreviousMonth(date, typeOfWork);
			scheduleCell.setActive(false);
		}
		return doctor;
	}
	
	private Doctor findDoctorScheduledOnDateFromPreviousMonth(Calendar date, TypeOfWork typeOfWork) {
		Work work = persistanceService.findWork(date, typeOfWork);
		if (work == null) {
			return null;
		}
		return work.getDoctor();
	}
	

	protected abstract Doctor findAvailableDoctor(Calendar date, TypeOfWork typeOfWork, ScheduleCell scheduleCell) throws Exception;
	protected abstract void secondPass(List<com.medistat.dialog.Schedule> schedules)  throws Exception;
	protected abstract void beforeCreation(Integer year, Calendar date);
	protected abstract void afterCreation();
}
